from Products.KnowledgeEngine.Components.FileUpload.FileUpload import FileUpload, FileUploadExceedsMaximumSizeException, MimeTypeIncorrectException, MIME_TYPES
from Products.KnowledgeEngine.Component import Component
import os, random

from Products.KnowledgeEngine.Utils.helpers import encodeJSON, decodeJSON

FileUploadExceedsMaximumSizeException

class UploadFilesystem:
    """ File upload handler to store files on the Filesystem """
    
    name  = "UploadFilesystem"
    title = "Upload to Filesytem"
   
    def __init__( self, context, assessment, formValue ):
       
        self.context = context
        self.assessment = assessment
        self.formValue  = formValue
   
   
   
    def getFilename(self, file):
        """ Gets the uploaded files filename """
        return getattr(file, 'filename', None)
   
    def getFileContents(self, file):
        """ Gets the file contents """
        
        file.seek(0)
        return file.read()
   
    def getFileHandle(self):
        """ Gets the file handle """
        
        return self.formValue

    def getContentType(self, file):
        """ Gets the content type """
        
        return file.headers['Content-Type'].lower()

    def getAllowedTypes(self):
        """ Returns the list of allowed types """
        
        types = self.context.getPropertyValue('allowedMimeTypes')
        if types:
            return [ t.lower() for t in types.split( "," ) ]
        else:
            return []


    def sanityCheck(self):
        """ Check everything is within our contraints for upload """
        
    
        file_contents = self.getFileContents()
        file_size = len(file_contents)

        # Check that the file is within our set bounnds
        if file_size > (self.context.getPropertyValue('maximumFilesize') * 1024):
            raise FileUploadExceedsMaximumSizeException('Uploaded file is too big', component=self.context)
    
        # Now check that the file is of an acceptable MIME type
        content_type    = self.getContentType()
        allowed_types   = self.getAllowedTypes()
        
        # If allowed_types is specified, only allow the desired mime types through (If not, let anything through)
        if allowed_types:                                            
            if (content_type not in allowed_types) and ( content_type.replace("x-","") not in allowed_types):
                raise MimeTypeIncorrectException('MIME Type incorrect - %s' % (content_type), component=self.context)


    @classmethod
    def getFilesystemDirectory( cls, component):
        """ Gets the filesystem directory """

        storage_directory = component.getPropertyValue('filesystemDirectory').strip()
            
        if not storage_directory:
            from App.config import getConfiguration
            storage_directory = getConfiguration().clienthome + "/kefiles/"
            
    
        if not os.path.exists( storage_directory ):
            os.mkdir( storage_directory )
            
        return storage_directory
        

                             
        
        
        

    def process(self):
        """ Process the file upload """ 
        assessment = self.assessment
        
        component_value = ""
        
        #filename = self.getFilename()
        #if filename:
        
        componentState = assessment.getComponentState(self.context.getIdentity())               
        metadata = UploadFilesystem.processMetadataFromStorage( componentState.getValue() )
        
        if 1: # Tmp
            
            storage_directory = UploadFilesystem.getFilesystemDirectory( self.context )
            
            #self.sanityCheck()
            for file in self.formValue:
        
                # Look for any files we need to remove (the 'Remove' checkbox has been checked)
                if type(file) is str and file.startswith("_CLEAR-"):
                    junk, clearIndex = file.split("-")
                    metadata[ int(clearIndex) ] = None
                    continue
        
                content_type = self.getContentType( file )
                file_contents = self.getFileContents( file )
                file_size     = len( file_contents )
            
                filename = self.getFilename( file )
                
                if not filename: continue # No file was actually uploaded for this field
            
                # The filename will be composed of the assessmentIdentity and componentId. This is
                # stored as the value.
                #
                # 20061221 johnm    If the MIME type is pre-registered, guess the file suffix
                if MIME_TYPES.has_key(content_type):
                    suffix = MIME_TYPES[content_type]
                else:
                    suffix = filename.split('.').pop()
                    
                                
                # Generate a unique filename
                basename = "%s_%s_%s_%s" % (assessment.getIdentity(), self.context.getIdentity(), filename, random.randint( 0, 99999 ))
    
                # Create a new file at the location given by the storage directory
                # and the constructed filename
                full_filename = "%s/%s" % (storage_directory, basename)
                uploaded_file = open(full_filename, "wb")
                uploaded_file.write(file_contents)
                uploaded_file.close()
    
                # Generate metadata to store in the database. (This ends up JSON encoded)                
                brain = dict( 
                    basename     = basename,
                    content_type = content_type,   
                    filename     = filename,
                    file_size    = file_size
                )
                
                metadata.append( brain )                
        
        # Purge the cleared items from the list
        metadata = filter(None, metadata)
        
        # JSON Encode the value 
        component_value = encodeJSON( metadata )
        
        self.setComponentValue( component_value )
            

    def setComponentValue( self, component_value ):
        """ Saves the value against the component """

        #get the current assessment state for the component from the assessment
        state = self.assessment.getComponentState(self.context.getIdentity())

        # Check that we're not overwriting the previous details with nothing
        #
        # TODO: How do we empty an uploaded file?
        previous_value = state.getValue()
        if (not component_value) and previous_value and (self.formValue != "CLEAR"):
            return

        Component.updateAssessmentState(self.context, self.assessment, component_value)
                
        #update the current state
        state.setValue(component_value)
        
        # If a searchIndex is defined, add that info to the component state
        searchIndex = getattr(self, 'searchIndex', False)
        if searchIndex:
            state.setSearchIndex(searchIndex)
            state.setSearchValue(component_value)
        
        #update the assessment
        self.assessment.setIsModified(True)
       
   

    @classmethod
    def download( cls, component, assessment, index ):
        """ Downloads the file """
        
        
        assessmentIdentity = assessment.getIdentity()
                
        securityService = component.knowledgeengine_securityservice
        
        downloadAllowed = False

        componentState = assessment.getComponentState(component.getIdentity())
        value = componentState.getValue()
        
        metadata = cls.processMetadataFromStorage( value )
        
        file = metadata[int(index)]
        directory = cls.getFilesystemDirectory( component )
        full_filename = "%s/%s" % ( directory, file["basename"] )

        # Get the file contents
        uploaded_file = open(full_filename, "rb")
        file_contents = uploaded_file.read()
        uploaded_file.close()

        # Set relevant headers to force a download with the correct meta type
        setHeader = component.REQUEST.RESPONSE.setHeader
        setHeader( 'content-type', file["content_type"] )
        setHeader( 'content-disposition', 'attachment;filename=%s' % (file["filename"], ) )

        return file_contents      
      

    @classmethod
    def processMetadataFromStorage(cls, value):
        """ Extracts file metadata from the database entry """
                        
        if not value:
            return []
                        
        if value.startswith("["): # Is new format 
            metadata = decodeJSON( value )
            return metadata
        else: # Old format - Used for backwards compatibility
            
            basename, content_type, filename, file_size = value.split(",")
            return [dict( basename     = basename,
                         content_type = content_type,
                         filename     = filename,
                         file_size    = file_size )]


FileUpload.registerUploadModule( UploadFilesystem )